Managing Posts
==============

Managing posts mainly refers to listing posts in an administrative view that allows us to see posts with all statuses, updating them and deleting them. They are accomplished by the `admin` operation and the `delete` operation, respectively. The code generated by `Gii` does not need much modification. Below we mainly explain how these two operations are implemented.


Listing Posts in Tabular View
-----------------------------

The `admin` operation shows posts with all statuses in a tabular view. The view supports sorting and pagination. The following is the `actionAdmin()` method in `PostController`:

~~~
[php]
public function actionAdmin()
{
	$model=new Post('search');
	if(isset($_GET['Post']))
		$model->attributes=$_GET['Post'];
	$this->render('admin',array(
		'model'=>$model,
	));
}
~~~

The above code is generated by the `Gii` tool without any modification. It first creates a `Post` model under the `search` [scenario](/doc/guide/form.model). We will use this model to collect the search conditions that the user specifies. We then assign to the model the user-supplied data, if any. Finally, we render the `admin` view with the model.

Below is the code for the `admin` view:

~~~
[php]
<?php
$this->breadcrumbs=array(
	'Manage Posts',
);
?>
<h1>Manage Posts</h1>

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'dataProvider'=>$model->search(),
	'filter'=>$model,
	'columns'=>array(
		array(
			'name'=>'title',
			'type'=>'raw',
			'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)'
		),
		array(
			'name'=>'status',
			'value'=>'Lookup::item("PostStatus",$data->status)',
			'filter'=>Lookup::items('PostStatus'),
		),
		array(
			'name'=>'create_time',
			'type'=>'datetime',
			'filter'=>false,
		),
		array(
			'class'=>'CButtonColumn',
		),
	),
)); ?>
~~~

We use [CGridView] to display the posts. It allows us to sort by a column and paginate through the posts if there are too many to be displayed in a single page. Our change is mainly about how to display each column. For example, for the `title` column, we specify that it should be displayed as a hyperlink that points to the detailed view of the post. The expression `$data->url` returns the value of the `url` property that we define in the `Post` class.

> Tip: When displaying text, we call [CHtml::encode()] to encode HTML entities in it. This prevents from [cross-site scripting attack](http://www.yiiframework.com/doc/guide/topics.security).


Deleting Posts
--------------

In the `admin` data grid, there is a delete button in each row. Clicking on the button should delete the corresponding post. Internally, this triggers the `delete` action implemented as follows:

~~~
[php]
public function actionDelete()
{
	if(Yii::app()->request->isPostRequest)
	{
		// we only allow deletion via POST request
		$this->loadModel()->delete();

		if(!isset($_GET['ajax']))
			$this->redirect(array('index'));
	}
	else
		throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');
}
~~~

The above code is the one generated by the `Gii` tool without any change. We would like to explain a little bit more about the checking on `$_GET['ajax']`. The [CGridView] widget has a very nice feature that its sorting, pagination and deletion operations are all done in AJAX mode by default. That means, the whole page does not get reloaded if any of the above operations is performed. However, it is also possible that the widget runs in non-AJAX mode (by setting its `ajaxUpdate` property to be false or disabling JavaScript on the client side). It is necessary for the `delete` action to differentiate these two scenarios: if the delete request is made via AJAX, we should not redirect the user browser; otherwise, we should.

Deleting a post should also cause the deletion of all comments for that post. In addition, we should also update the `tbl_tag` table regarding the tags for the deleted post. Both of these tasks can be achieved by writing an `afterDelete` method in the `Post` model class as follows,

~~~
[php]
protected function afterDelete()
{
	parent::afterDelete();
	Comment::model()->deleteAll('post_id='.$this->id);
	Tag::model()->updateFrequency($this->tags, '');
}
~~~

The above code is very straightforward: it first deletes all those comments whose `post_id` is the same as the ID of the deleted post; it then updates the `tbl_tag` table for the `tags` of the deleted post.

> Tip: We have to explicitly delete all comments for the deleted post here because SQLite does not really support foreign key constraints. In a DBMS that supports this constraint (such as MySQL, PostgreSQL), the foreign key constraint can be set up such that the DBMS automatically deletes the related comments if the post is deleted. In that case, we no longer this explicit deletion call in our code.

